home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / Python 133 68K / Demo / www / wwww.py < prev   
Text File  |  1996-05-20  |  23KB  |  903 lines

  1. #! /usr/local/bin/python
  2.  
  3. # A stdwin-based Windowing WWW client (hence 4 W's!)
  4.  
  5.  
  6. import sys
  7. sys.path.append('/ufs/guido/src/www')
  8. import os
  9. import string
  10. import marshal
  11. import getopt
  12. import stdwin
  13. import mainloop
  14. from stdwinevents import *
  15. import fmt
  16. import htmllib
  17. import wwwlib
  18. import wwwutil
  19.  
  20.  
  21. # Parametrizations
  22. MAXHIST = 40                # Max number of history entries shown
  23. MAXANCH = 40                # Max number of anchors shown
  24. WAITCURSOR = 'watch'
  25. READYCURSOR = 'arrow'
  26.  
  27.  
  28. # Default locations of logging files
  29. PLACES = '.wwwplaces'
  30. HISTORY = '.wwwhistory'
  31. BOOKMARKS = '.wwwbookmarks'
  32.  
  33.  
  34. # Globals managed by main
  35. places = {}
  36. bookmarks = []
  37. history_file = ''
  38. bookmarks_menu = None
  39.  
  40.  
  41. # Enums indicating what to do with history when following a link
  42. H_POP = -1
  43. H_NOP = 0
  44. H_PUSH = 1
  45.  
  46.  
  47. def main():
  48.     global places, bookmarks
  49.     global history_file, places_file, bookmarks_file
  50.     #
  51.     help = 0
  52.     raw_mode = 0
  53.     server_mode = 0
  54.     #
  55.     places_file = ''
  56.     bookmarks_file = ''
  57.     #
  58.     try:
  59.         opts, args = getopt.getopt(sys.argv[1:], 'b:h:p:rsH')
  60.     except getopt.error, msg:
  61.         sys.stdout = sys.stderr
  62.         print msg
  63.         print \
  64.  'usage: wwww [-b file] [-h file] [-p file] [-r] [-H] [docaddr ...]'
  65.         print '-b file: bookmarks file, default ~/' + BOOKMARKS
  66.         print '-h file: history file, default ~/' + HISTORY
  67.         print '-p file: history file, default ~/' + PLACES
  68.         print '-r     : start windows in raw mode'
  69.         print '-s     : start in server mode'
  70.         print '-H     : start up with help window'
  71.         print 'docaddr: document address, e.g. scheme://host:port/path'
  72.         print 'system default:', wwwutil.system_home
  73.         if os.environ.has_key('WWW_HOME'):
  74.             print 'user default:', wwwutil.user_home
  75.         print 'user default settable with $WWW_HOME'
  76.         sys.exit(2)
  77.     #
  78.     for o, a in opts:
  79.         if o == '-b': bookmarks_file = a
  80.         if o == '-h': history_file = a
  81.         if o == '-p': places_file = a
  82.         if o == '-r': raw_mode = 1
  83.         if o == '-s': server_mode = 1
  84.         if o == '-H': help = 1
  85.     #
  86.     places = wwwutil.load_file(places_file, PLACES)
  87.     if places is None: places = {}
  88.     #
  89.     bookmarks = wwwutil.load_file(bookmarks_file, BOOKMARKS)
  90.     if bookmarks is None: bookmarks = []
  91.     if wwwutil.user_home and wwwutil.user_home not in bookmarks:
  92.         bookmarks.insert(0, wwwutil.user_home)
  93.     if wwwutil.system_home and wwwutil.system_home not in bookmarks:
  94.         bookmarks.insert(0, wwwutil.system_home)
  95.     #
  96.     history = wwwutil.load_file(history_file, HISTORY)
  97.     if history is None: history = []
  98.     if not server_mode and not args and history:
  99.         args.append(history[-1])
  100.         del history[-1]
  101.     #
  102.     if server_mode:
  103.         start_server()
  104.     #
  105.     stdwin.setdefwinsize(0, 0)
  106.     for addr in args:
  107.         w = WWWWindow()
  108.         w.set_raw_mode(raw_mode)
  109.         if not w.setaddr(addr):
  110.             w.close()
  111.         else:
  112.             w.set_history(history)
  113.     #
  114.     if not server_mode and mainloop.countwindows() == 0:
  115.         # Fallback -- try default pages
  116.         for addr in wwwutil.user_home, wwwutil.system_home:
  117.             if addr not in args:
  118.                 w = WWWWindow()
  119.                 w.set_raw_mode(raw_mode)
  120.                 if not w.setaddr(addr):
  121.                     w.close()
  122.                 else:
  123.                     w.set_history(history)
  124.                     break
  125.     #
  126.     if help or not server_mode and mainloop.countwindows() == 0:
  127.         make_help_window()
  128.     #
  129.     mainloop.mainloop()
  130.  
  131.  
  132. # Start the help server (for -s option: server_mode)
  133. server_socket = None
  134. def start_server():
  135.     global server_socket
  136.     import socket
  137.     server_socket = s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  138.     try:
  139.         s.bind('', 4000)
  140.     except socket.error, msg:
  141.         if type(msg) == type(()) and len(msg) == 2 and msg[0] == 114:
  142.             print 'A help server is already running -- exit.'
  143.             sys.exit(0)
  144.         else:
  145.             raise socket.error, msg
  146.     print 'Starting server mode'
  147.     mainloop.registerfd(s, 'r', handle_server_message)
  148.  
  149.  
  150. # Handle a server message.
  151. # Format is '<wname> <function> <argument> ...'
  152. # This first looks for a window named <wname>, and opens one if necessary.
  153. # Then it calls the window's method msg_<function> with the argument
  154. # list as argument, as a list of strings.
  155. def handle_server_message(*args):
  156.     data, clientaddr = server_socket.recvfrom(1024)
  157.     words = string.split(data)
  158.     if len(words) < 2:
  159.         print 'Bad message from', clientaddr[0], ':', `data`
  160.         return
  161.     wname = words[0]
  162.     function = words[1]
  163.     try:
  164.         meth = getattr(WWWWindow, 'msg_' + function)
  165.     except AttributeError:
  166.         print 'Bad function', `function`, 'in message from', \
  167.             clientaddr[0], ':', `data`
  168.         return
  169.     ##print 'Message from', clientaddr[0], ':', `data`
  170.     new = 0
  171.     for w in allwindows:
  172.         if w.server_name == wname:
  173.             break
  174.     else:
  175.         print 'Creating new window named', `wname`
  176.         stdwin.setdefwinsize(0, 0)
  177.         w = WWWWindow()
  178.         w.server_name = wname
  179.         new = 1
  180.     try:
  181.         getattr(w, 'msg_' + function)(words[2:], clientaddr)
  182.     except:
  183.         print '***', sys.exc_type, ':', `sys.exc_value`
  184.  
  185.  
  186. # Make the menu of bookmarks
  187. def make_bookmarks_menu(raw_mode):
  188.     global bookmarks_menu
  189.     if bookmarks_menu is not None:
  190.         bookmarks_menu.close()
  191.         bookmarks_menu = None
  192.     bookmarks_menu = menu = stdwin.menucreate('Bookmarks')
  193.     for addr in bookmarks:
  194.         if not raw_mode and places.has_key(addr):
  195.             t = places[addr][0]
  196.             if string.strip(t): addr = t
  197.         menu.additem(addr)
  198.  
  199.  
  200. # Misc. globals
  201. charnames = {'?': 'question', ' ': 'space'}
  202. allwindows = []
  203. last_regex = None
  204.  
  205.  
  206. class WWWWindow:
  207.     #
  208.     def __init__(self):
  209.         # For server
  210.         self.server_name = None
  211.         # Windowing state
  212.         self.window = stdwin.open('WWWW - by Guido van Rossum')
  213.         self.window.setwincursor(WAITCURSOR)
  214.         self.last_mouse_down = None
  215.         self.last_msg = ''
  216.         # Current document state
  217.         self.cur_addr = ''
  218.         self.cur_data = ''
  219.         self.cur_anchors = []
  220.         self.cur_anchornames = []
  221.         self.cur_anchortypes = []
  222.         self.cur_isindex = 0
  223.         self.cur_backend = None
  224.         # User state
  225.         self.raw_mode = 0
  226.         # Logging state
  227.         self.history = []
  228.         # Menus
  229.         self.history_menu = None
  230.         self.anchors_menu = None
  231.         self.make_command_menu()
  232.         self.make_bookmarks_menu()
  233.         # Register us for events
  234.         self.window.dispatch = self.dispatch
  235.         mainloop.register(self.window)
  236.         allwindows.append(self)
  237.         # Initialize cur_backend
  238.         self.setrawdata('', '')
  239.     #
  240.     def make_command_menu(self):
  241.         self.command_menu = menu = self.window.menucreate('Commands')
  242.         self.command_call = []
  243.         #
  244.         self.add_cmd('Help', '?', self.key_question)
  245.         #
  246.         self.add_cmd('', '', None) # ----------------------------------
  247.         #
  248.         self.find_index = len(self.command_call)
  249.         self.add_cmd('Find keyword(s) in index...', 'i', self.key_i)
  250.         menu.enable(self.find_index, self.cur_isindex)
  251.         #
  252.         self.add_cmd('Find regexp...', 'f', self.key_f)
  253.         #
  254.         self.add_cmd('Find aGain', 'g', self.key_g)
  255.         #
  256.         self.add_cmd('', '', None) # ----------------------------------
  257.         #
  258.         self.back_index = len(self.command_call)
  259.         self.add_cmd('Back up', 'b', self.key_b)
  260.         menu.enable(self.back_index, len(self.history) > 0)
  261.         #
  262.         self.add_cmd('User home', 'H', self.key_H)
  263.         #
  264.         self.add_cmd('System home', 'S', self.key_S)
  265.         #
  266.         self.add_cmd('Go to ...', 'G', self.key_G)
  267.         #
  268.         self.add_cmd('', '', None) # ----------------------------------
  269.         #
  270.         self.add_cmd('Up', 'u', self.key_u)
  271.         #
  272.         self.add_cmd('Down', 'd', self.key_d)
  273.         #
  274.         self.add_cmd('Next', 'n', self.key_n)
  275.         #
  276.         self.add_cmd('Prev', 'p', self.key_p)
  277.         #
  278.         self.add_cmd('Traverse (space)', ' ', self.key_space)
  279.         #
  280.         self.add_cmd('', '', None) # ----------------------------------
  281.         #
  282.         self.raw_index = len(self.command_call)
  283.         self.add_cmd('Show raw addresses', 'r', self.key_r)
  284.         menu.check(self.raw_index, self.raw_mode)
  285.         #
  286.         self.add_cmd('Add to Bookmarks menu', 'a', self.key_a)
  287.         #
  288.         self.add_cmd('', '', None) # ----------------------------------
  289.         #
  290.         self.add_cmd('Clone window', 'k', self.key_k)
  291.         #
  292.         self.add_cmd('Show source window', 's', self.key_s)
  293.         #
  294.         self.add_cmd('', '', None) # ----------------------------------
  295.         #
  296.         self.add_cmd('Save...', '', self.save)
  297.         #
  298.         self.add_cmd('Save source...', '', self.save_source)
  299.         #
  300.         self.add_cmd('', '', None) # ----------------------------------
  301.         #
  302.         self.add_cmd('Quit (close window)', 'q', self.key_q)
  303.     #
  304.     def add_cmd(self, label, key, func):
  305.         self.command_call.append(func)
  306.         if key:
  307.             self.command_menu.additem(label, key)
  308.         else:
  309.             self.command_menu.additem(label)
  310.     #
  311.     def set_raw_mode(self, raw_mode):
  312.         self.raw_mode = raw_mode
  313.         self.make_title()
  314.         self.command_menu.check(self.raw_index, self.raw_mode)
  315.         self.make_anchors_menu()
  316.         self.make_history_menu() # Implies make_bookmarks_menu
  317.     #
  318.     def set_history(self, history):
  319.         self.history = history[:]
  320.         self.make_history_menu()
  321.     #
  322.     def new(self):
  323.         stdwin.setdefwinsize(self.window.getwinsize())
  324.         w = WWWWindow()
  325.         w.set_raw_mode(self.raw_mode)
  326.         return w
  327.     #
  328.     def close(self):
  329.         if self in allwindows:
  330.             allwindows.remove(self)
  331.         mainloop.unregister(self.window)
  332.         self.window.dispatch = None
  333.         self.window.close()
  334.         if self.history:
  335.             wwwutil.save_file(self.history + [self.cur_addr], \
  336.                       history_file, HISTORY)
  337.         wwwutil.save_file(bookmarks, bookmarks_file, BOOKMARKS)
  338.         wwwutil.save_file(places, places_file, PLACES)
  339.     #
  340.     def dispatch(self, event):
  341.         type, win, detail = event
  342.         if type == WE_CLOSE:
  343.             self.close()
  344.         if type == WE_SIZE:
  345.             self.resize()
  346.         if type == WE_DRAW:
  347.             self.redraw(detail)
  348.         if type == WE_CHAR:
  349.             self.character(detail)
  350.         if type == WE_COMMAND:
  351.             if detail == WC_CANCEL:
  352.                 self.close()
  353.         if type == WE_MOUSE_DOWN:
  354.             self.mouse_down(detail)
  355.         if type == WE_MOUSE_MOVE:
  356.             self.mouse_move(detail)
  357.         if type == WE_MOUSE_UP:
  358.             self.mouse_up(detail)
  359.         if type == WE_MENU:
  360.             self.menu(detail)
  361.     #
  362.     def menu(self, (menu, item)):
  363.         if menu is self.command_menu:
  364.             self.command_call[item]()
  365.         if menu is self.anchors_menu:
  366.             self.follow(self.cur_anchors[item])
  367.         if menu is self.history_menu:
  368.             offset = max(0, len(self.history) - MAXHIST)
  369.             if 0 <= offset + item < len(self.history):
  370.                 self.follow(self.history[offset + item], H_POP)
  371.         if menu is bookmarks_menu:
  372.             self.follow(bookmarks[item])
  373.     #
  374.     def mouse_down(self, detail):
  375.         h, v = detail[0]
  376.         button = detail[2]
  377.         mask = detail[3]
  378.         if not mask&(WM_SHIFT|WM_CONTROL):
  379.             hits = self.cur_backend.hitcheck(h, v)
  380.             if hits:
  381.                 # Anchor hit -- triggered on mouse up
  382.                 self.last_mouse_down = None
  383.                 return
  384.         long1 = long2 = self.cur_backend.whereis(stdwin, h, v)
  385.         if not long1:
  386.             self.last_mouse_down = None
  387.             self.cur_backend.setselection(None)
  388.             return
  389.         if (button == 3 or mask&WM_SHIFT) and self.last_mouse_down:
  390.             pass # Remember the old last_mouse_down
  391.         else:
  392.             self.last_mouse_down = detail
  393.         long1, long2 = self.roundpositions(long1, long2)
  394.         self.cur_backend.setselection((long1, long2))
  395.     #
  396.     def roundpositions(self, long1, long2):
  397.         if long1 > long2:
  398.             long1, long2 = long2, long1
  399.         clicks = self.last_mouse_down[1]
  400.         if clicks > 2:
  401.             long1, long2 = \
  402.                 self.cur_backend.roundtoparagraphs(long1, long2)
  403.         elif clicks > 1:
  404.             long1, long2 = \
  405.                 self.cur_backend.roundtowords(long1, long2)
  406.         return long1, long2
  407.     #
  408.     def mouse_move(self, detail):
  409.         if not self.last_mouse_down:
  410.             return
  411.         h1, v1 = self.last_mouse_down[0]
  412.         h2, v2 = detail[0]
  413.         long1 = self.cur_backend.whereis(stdwin, h1, v1)
  414.         long2 = self.cur_backend.whereis(stdwin, h2, v2)
  415.         if long1 and long2:
  416.             long1, long2 = self.roundpositions(long1, long2)
  417.             self.cur_backend.setselection((long1, long2))
  418.     #
  419.     def mouse_up(self, detail):
  420.         if self.last_mouse_down:
  421.             h1, v1 = self.last_mouse_down[0]
  422.             h2, v2 = detail[0]
  423.             long1 = self.cur_backend.whereis(stdwin, h1, v1)
  424.             long2 = self.cur_backend.whereis(stdwin, h2, v2)
  425.             if not long1 or not long2:
  426.                 return
  427.             long1, long2 = self.roundpositions(long1, long2)
  428.             self.cur_backend.setselection((long1, long2))
  429.             #
  430.             selection = self.cur_backend.extractpart(long1, long2)
  431.             if not selection:
  432.                 return
  433.             if not self.window.setselection(WS_PRIMARY, selection):
  434.                 # Meaning some other application got it now
  435.                 stdwin.fleep()
  436.                 return
  437.             stdwin.rotatecutbuffers(1)
  438.             stdwin.setcutbuffer(0, selection)
  439.             return
  440.         h, v = detail[0]
  441.         hits = self.cur_backend.hitcheck(h, v)
  442.         if not hits:
  443.             return
  444.         if len(hits) > 1:
  445.             stdwin.message('Please click in exactly one anchor')
  446.             return
  447.         id = hits[0]
  448.         if 1 <= id <= len(self.cur_anchors):
  449.             addr = self.cur_anchors[id-1]
  450.             name = self.cur_anchornames[id-1]
  451.             type = self.cur_anchortypes[id-1]
  452.             button = detail[2]
  453.             if button == 2:
  454.                 if name:
  455.                     msg = 'NAME="' + name + '" '
  456.                 else:
  457.                     msg = ''
  458.                 if addr:
  459.                     msg = msg + 'HREF="' + addr + '"'
  460.                 stdwin.message(msg)
  461.             elif addr[:7] == 'telnet:':
  462.                 self.telnet(addr)
  463.             elif button == 3 and addr and addr[0] <> '#':
  464.                 addr = self.full_addr(addr)
  465.                 w = self.new()
  466.                 history = self.history + [self.cur_addr]
  467.                 if not w.setaddr(addr):
  468.                     w.close()
  469.                     stdwin.message(addr + ': ' + \
  470.                         self.last_msg)
  471.                 else:
  472.                     w.set_history(history)
  473.             else:
  474.                 self.follow(addr)
  475.         else:
  476.             stdwin.message('Strange - bad anchor id???')
  477.     #
  478.     def character(self, c):
  479.         if charnames.has_key(c):
  480.             c = charnames[c]
  481.         try:
  482.             method = getattr(self, 'key_' + c)
  483.         except AttributeError:
  484.             stdwin.fleep()
  485.             return
  486.         method()
  487.     #
  488.     # Message handlers for server mode
  489.     #
  490.     def msg_null(self, args, clientaddr):
  491.         pass
  492.     #
  493.     def msg_close(self, args, clientaddr):
  494.         self.close()
  495.     #
  496.     def msg_echo(self, args, clientaddr):
  497.         reply = self.server_name + ' echo ' + string.join(args)
  498.         server_socket.sendto(reply, clientaddr)
  499.     #
  500.     def msg_goto(self, args, clientaddr):
  501.         if len(args) < 1:
  502.             print 'msg_goto: No address given'
  503.             return
  504.         addr = self.full_addr(args[0])
  505.         if addr <> self.cur_addr and \
  506.                 not self.followok(addr, H_PUSH, 1):
  507.             print 'msg_goto: Follow of', addr, 'failed'
  508.             return
  509.         if args[1:]:
  510.             self.find(args[1])
  511.     #
  512.     def msg_find(self, args, clientaddr):
  513.         if not args: pat = ''
  514.         else: pat = args[0]
  515.         self.find(pat)
  516.     #
  517.     # Special functions for Emacs INFO style navigation in documents
  518.     # generated by texi2html.py
  519.     #
  520.     def gotype(self, type, hist):
  521.         if type in self.cur_anchortypes:
  522.             i = self.cur_anchortypes.index(type)
  523.             addr = self.cur_anchors[i]
  524.             if addr:
  525.                 return self.followok(addr, hist, 1)
  526.         return 0
  527.     #
  528.     def gobytype(self, type, hist):
  529.         if not self.gotype(type, hist):
  530.             stdwin.fleep()
  531.     #
  532.     def key_space(self): # traverse
  533.         if self.gotype('menu', H_PUSH) or self.gotype('next', H_NOP):
  534.             return
  535.         while 1:
  536.             if not self.gotype('up', H_POP):
  537.                 stdwin.fleep()
  538.                 return
  539.             if self.gotype('next', H_NOP):
  540.                 return
  541.     #
  542.     def key_u(self): # up
  543.         self.gobytype('up', H_POP)
  544.     #
  545.     def key_d(self): # down
  546.         self.gobytype('menu', H_PUSH)
  547.     #
  548.     def key_n(self): # next
  549.         self.gobytype('next', H_NOP)
  550.     #
  551.     def key_p(self): # prev
  552.         self.gobytype('prev', H_NOP)
  553.     #
  554.     def key_t(self): # top
  555.         self.gobytype('top', H_POP)
  556.     #
  557.     # Other keyboard shortcut handlers
  558.     #
  559.     def key_a(self): # add to global list of bookmarks
  560.         if self.cur_addr in bookmarks:
  561.             bookmarks.remove(self.cur_addr)
  562.         bookmarks.append(self.cur_addr)
  563.         self.make_bookmarks_menu()
  564.     #
  565.     def key_f(self): # Find string
  566.         if last_regex:
  567.             prog, pat = last_regex
  568.         else:
  569.             prog, pat = None, ''
  570.         try:
  571.             pat = stdwin.askstr('Enter regular expression:', pat)
  572.         except KeyboardInterrupt:
  573.             return
  574.         self.find(pat)
  575.     #
  576.     def key_g(self): # find aGain
  577.         self.find('')
  578.     #
  579.     def find(self, pat):
  580.         import regex
  581.         global last_regex
  582.         if not pat:
  583.             if last_regex:
  584.                 prog, pat = last_regex
  585.             else:
  586.                 prog, pat = None, ''
  587.             if not prog:
  588.                 stdwin.message('No previous regexp')
  589.                 return
  590.         else:
  591.             try:
  592.                 prog = regex.compile(string.lower(pat))
  593.             except regex.error, msg:
  594.                 if type(msg) <> type(''): msg = `msg`
  595.                 stdwin.message(msg)
  596.                 return
  597.             last_regex = prog, pat
  598.         if not self.cur_backend.search(prog):
  599.             stdwin.fleep()
  600.     #
  601.     def key_H(self): # User home
  602.         self.follow(wwwutil.user_home)
  603.     #
  604.     def key_S(self): # System home
  605.         self.follow(wwwutil.system_home)
  606.     #
  607.     def key_b(self): # back
  608.         self.back()
  609.     #
  610.     def key_i(self): # find keyword in index
  611.         try:
  612.             reply = stdwin.askstr('Find keyword(s) in index:', '')
  613.         except KeyboardInterrupt:
  614.             return
  615.         words = string.split(reply)
  616.         if not words:
  617.             return
  618.         scheme, host, port, path, search, anchor = \
  619.             wwwlib.parse_addr(self.cur_addr)
  620.         if len(search) > 1: hist = H_NOP
  621.         else: hist = H_PUSH
  622.         search = '?' + string.joinfields(words, '+')
  623.         addr = wwwlib.unparse_addr(scheme,host,port,path,search,'')
  624.         self.follow(addr, hist)
  625.     #
  626.     def key_G(self): # goto
  627.         try:
  628.             reply = stdwin.askstr('Go to document:', self.cur_addr)
  629.         except KeyboardInterrupt:
  630.             return
  631.         addr = string.strip(reply)
  632.         if not addr:
  633.             return
  634.         self.follow(addr)
  635.     #
  636.     def key_h(self): # help
  637.         make_help_window()
  638.     key_question = key_h
  639.     #
  640.     def key_k(self): # clone
  641.         w = self.new()
  642.         w.setrawdata(self.cur_addr, self.cur_data)
  643.         w.set_history(self.history)
  644.     #
  645.     def key_q(self): # quit (closes current window only)
  646.         self.close()
  647.     #
  648.     def key_r(self): # raw (toggle)
  649.         self.set_raw_mode(not self.raw_mode)
  650.     #
  651.     def key_s(self): # source
  652.         w = self.new()
  653.         w.setrawdata(self.cur_addr, '<PLAINTEXT>' + self.cur_data)
  654.         w.set_raw_mode(1)
  655.     #
  656.     def telnet(self, addr):
  657.         scheme, host, port, path, search, anchor = \
  658.             wwwlib.parse_addr(addr)
  659.         if scheme <> 'telnet':
  660.             print 'Not a telnet address'
  661.             return
  662.         cmd = 'telnet ' + host
  663.         cmd = 'xterm -e ' + cmd + ' &'
  664.         sts = os.system(cmd)
  665.         if sts:
  666.             print `cmd`
  667.             print 'Exit status:', sts
  668.     #
  669.     def follow(self, addr, *args):
  670.         # Process optional arguments [hist, [warn]]
  671.         if args: hist = args[0]
  672.         else: hist = H_PUSH
  673.         if args[1:]: warn = args[1]
  674.         else: warn = 1
  675.         dummy = self.followok(addr, hist, warn)
  676.     #
  677.     def followok(self, addr, hist, warn):
  678.         # Check for empty address
  679.         if not addr: return 1 # We're already here
  680.         # Check if there's just an anchor
  681.         if addr[0] == '#':
  682.             anchor = addr[1:]
  683.             if anchor not in self.cur_anchornames:
  684.                 if warn:
  685.                     stdwin.fleep() # Anchor not found
  686.                 return 0
  687.             id = 1 + self.cur_anchornames.index(anchor)
  688.             self.cur_backend.showanchor(id)
  689.             return 1
  690.         # Set the address, bail out if we can't
  691.         parent = self.cur_addr
  692.         if not self.setaddr(addr):
  693.             if warn:
  694.                 stdwin.message(addr + ' :' + self.last_msg)
  695.             return 0
  696.         # Fix the exits in the places directory
  697.         exits = places[parent][1]
  698.         if self.cur_addr in exits:
  699.             exits.remove(self.cur_addr)
  700.         exits.append(self.cur_addr)
  701.         # Fix the history
  702.         if hist == H_NOP:
  703.             pass
  704.         elif hist == H_POP:
  705.             if self.history and self.history[-1] == self.cur_addr:
  706.                 del self.history[-1]
  707.         elif hist == H_PUSH:
  708.             if parent:
  709.                 self.history.append(parent)
  710.         else:
  711.             print 'Strange hist:', hist
  712.         self.make_history_menu()
  713.         return 1
  714.     #
  715.     def back(self):
  716.         if not self.history:
  717.             stdwin.fleep()
  718.             return
  719.         self.follow(self.history[-1], H_POP)
  720.     #
  721.     def save(self):
  722.         try:
  723.             file = stdwin.askfile('Save to file:', '', 1)
  724.         except KeyboardInterrupt:
  725.             return
  726.         file = string.strip(file)
  727.         if not file:
  728.             return
  729.         try:
  730.             f = open(file, 'w')
  731.         except IOError, msg:
  732.             stdwin.message(file + ': ' + msg)
  733.             return
  734.         self.window.setwincursor(WAITCURSOR)
  735.         fmtr = fmt.WritingFormatter(f, 72)
  736.         parser = htmllib.FormattingParser(fmtr, \
  737.                               htmllib.NullStylesheet)
  738.         try:
  739.             parser.feed(self.cur_data)
  740.             parser.close()
  741.             f.write('\n')
  742.             f.close()
  743.         except IOError, msg:
  744.             stdwin.message(file + ': ' + msg)
  745.         self.window.setwincursor(READYCURSOR)
  746.     #
  747.     def save_source(self):
  748.         try:
  749.             file = stdwin.askfile('Save source to file:', '', 1)
  750.         except KeyboardInterrupt:
  751.             return
  752.         file = string.strip(file)
  753.         if not file:
  754.             return
  755.         try:
  756.             f = open(file, 'w')
  757.             f.write(self.cur_data)
  758.             f.close()
  759.         except IOError, msg:
  760.             stdwin.message(file + ': ' + msg)
  761.     #
  762.     def setaddr(self, addr):
  763.         self.window.setwincursor(WAITCURSOR)
  764.         savetitle = self.window.gettitle()
  765.         full = self.full_addr(addr)
  766.         self.window.settitle('Retrieving ' + full + ' ...')
  767.         try:
  768.             data = wwwlib.get_document(full)
  769.         except:
  770.             msg = sys.exc_value
  771.             if type(msg) <> type(''):
  772.                 msg = `msg`
  773.             self.last_msg = msg
  774.             self.window.settitle(savetitle)
  775.             self.window.setwincursor(READYCURSOR)
  776.             return 0
  777.         self.setrawdata(full, data)
  778.         return 1
  779.     #
  780.     def setrawdata(self, addr, data):
  781.         if '#' in addr:
  782.             i = string.index(addr, '#')
  783.             addr, anchor = addr[:i], addr[i+1:]
  784.         else:
  785.             anchor = ''
  786.         self.cur_addr = addr
  787.         self.cur_data = data
  788.         #
  789.         self.window.settitle('Formatting ' + addr + ' ...')
  790.         self.window.setorigin(0, 0)
  791.         self.window.setdocsize(0, 0)
  792.         self.window.change((0, 0), (10000, 30000)) # XXX
  793.         #
  794.         # XXX The X11 version here assumes that setdocsize()
  795.         # generates a WE_DRAW event
  796.         b = fmt.StdwinBackEnd(self.window, 0)
  797.         b.d.setfont(htmllib.StdwinStylesheet.stdfontset[0])
  798.         ##b = fmt.StdwinBackEnd(self.window, 1) # Mac version
  799.         f = fmt.BaseFormatter(b.d, b)
  800.         p = htmllib.AnchoringParser(f, htmllib.StdwinStylesheet)
  801.         p.feed(self.cur_data)
  802.         p.close()
  803.         b.finish()
  804.         #
  805.         self.cur_backend = b
  806.         self.cur_anchors = p.anchors
  807.         self.cur_anchornames = p.anchornames
  808.         self.cur_anchortypes = p.anchortypes
  809.         self.cur_isindex = p.isindex
  810.         #
  811.         if not places.has_key(self.cur_addr):
  812.             exits = []
  813.         else:
  814.             exits = places[self.cur_addr][1]
  815.         places[self.cur_addr] = (p.title, exits)
  816.         #
  817.         self.command_menu.enable(self.find_index, self.cur_isindex)
  818.         self.make_title()
  819.         self.make_anchors_menu()
  820.         self.window.setwincursor(READYCURSOR)
  821.         #
  822.         if anchor:
  823.             if anchor not in self.cur_anchornames:
  824.                 stdwin.fleep() # Anchor not found
  825.                 return
  826.             id = 1 + self.cur_anchornames.index(anchor)
  827.             self.cur_backend.showanchor(id)
  828.             return
  829.     #
  830.     def make_title(self):
  831.         title = self.nice_addr(self.cur_addr)
  832.         if self.cur_isindex:
  833.             title = title + ' (INDEX)'
  834.         self.window.settitle(title)
  835.     #
  836.     def make_anchors_menu(self):
  837.         if self.anchors_menu:
  838.             self.anchors_menu.close()
  839.             self.anchors_menu = None
  840.         if not self.cur_anchors:
  841.             return
  842.         self.anchors_menu = menu = self.window.menucreate('Links')
  843.         for addr in self.cur_anchors[:MAXANCH]:
  844.             menu.additem(self.nice_addr(addr))
  845.     #
  846.     def make_history_menu(self):
  847.         if self.history_menu:
  848.             self.history_menu.close()
  849.             self.history_menu = None
  850.         self.command_menu.enable(self.back_index, (self.history <> []))
  851.         self.history_menu = menu = self.window.menucreate('History')
  852.         i = 0
  853.         for addr in self.history[-MAXHIST:]:
  854.             menu.additem(self.nice_addr(addr))
  855.             i = i+1
  856.         menu.additem(self.nice_addr(self.cur_addr))
  857.         menu.check(i, 1)
  858.         self.make_bookmarks_menu()
  859.     #
  860.     def make_bookmarks_menu(self):
  861.         make_bookmarks_menu(self.raw_mode)
  862.     #
  863.     def resize(self):
  864.         self.cur_backend.resize()
  865.     #
  866.     def redraw(self, area):
  867.         if self.cur_backend:
  868.             self.window.setwincursor(WAITCURSOR)
  869.             self.cur_backend.redraw(area)
  870.             self.window.setwincursor(READYCURSOR)
  871.     #
  872.     def full_addr(self, addr):
  873.         return wwwlib.full_addr(self.cur_addr, addr)
  874.     #
  875.     def nice_addr(self, addr):
  876.         if self.raw_mode:
  877.             return addr
  878.         else:
  879.             full = self.full_addr(addr)
  880.             if places.has_key(full):
  881.                 t = places[full][0]
  882.                 if string.strip(t): return t
  883.             return addr
  884.  
  885.  
  886. def make_help_window():
  887.     stdwin.setdefwinsize(500, 350)
  888.     w = WWWWindow()
  889.     d = '<TITLE>WWWW Help</TITLE>\n'
  890.     d = d + '<H1>WWWW User Interface Help</H1>\n'
  891.     d = d + 'Underlined text is linked to other documents.<P>\n'
  892.     d = d + 'Click left to follow a link.<P>\n'
  893.     d = d + 'Click middle to show where a link leads.<P>\n'
  894.     d = d + 'Click right to follow a link in a new window.<P>\n'
  895.     w.setrawdata('builtin:help', d)
  896.  
  897.  
  898. try:
  899.     main()
  900. except KeyboardInterrupt:
  901.     print
  902.     print '[Intr]'
  903.